﻿using System.Collections.Generic;
using System.IO;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Xml;
using visinia.SmartEngine.Providers;

namespace visinia.SmartEngine.Installer
{
    internal class PluginInstaller
    {
        #region Installer
        internal static bool HandlePlugin(string viFile, List<ContentFileInfo> ContentFileList, XmlDocument doc, XmlNode typeNode)
        {
            XmlNode dataNode = typeNode["data"];
            if (dataNode != null)
            {
                // the plugin must have a name;
                XmlNode nameNode = dataNode["name"];
                if (nameNode == null)
                    return false;
                string pluginName = nameNode.InnerText;
                if (string.IsNullOrEmpty(pluginName))
                    return false;
                                
                // if this plugin is not specified in content file don't install it either
                ContentFileInfo cfInfo = ContentFileList.Find
                    (p => ((p.Type == ContentFileType.Plugin) &&
                           (string.Compare(p.Name, pluginName, true) == 0) &&
                           (p.IsInstalled == false)));

                if (cfInfo == null)
                    return false;
                // this plugin is already installed
                if (cfInfo.IsInstalled)
                    return false;
                
                // register plugin                        
                Plugin plugin = new Plugin();
                plugin.Name = pluginName;
                plugin.Description = dataNode["description"] != null ? dataNode["description"].InnerText : null;
                plugin.UrlSeparator = dataNode["urlseparator"] != null ? dataNode["urlseparator"].InnerText : null;
                plugin.UrlRewriteController = dataNode["urlrewritecontroller"] != null ? dataNode["urlrewritecontroller"].InnerText : null;
                plugin.SiteMapController = dataNode["sitemapcontroller"] != null ? dataNode["sitemapcontroller"].InnerText : null;
                plugin.Version = dataNode["version"] != null ? dataNode["version"].InnerText : null;
                PluginController.AddPlugin(plugin);

                string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
                string UninstallDirectoryName = UninstallRoot + pluginName;
                Directory.CreateDirectory(UninstallDirectoryName);
                // save this installtion data file also for uninstallation needs                            
                string installationDataFileBkup = Path.Combine(UninstallDirectoryName, Path.GetFileName(viFile));
                doc.Save(installationDataFileBkup);

                XmlNode filesNode = typeNode["files"];
                if (filesNode != null)
                {
                    foreach (XmlNode fileNode in filesNode.ChildNodes)
                    {
                        // create files and directories in proper website folders  
                        string directory = Path.GetDirectoryName(viFile);
                        string sourcePath = Path.Combine(directory, fileNode.InnerText);
                        if (!File.Exists(sourcePath))
                            continue;
                        switch (fileNode.Name.ToLower())
                        {
                            case "modules":// copy files to the site modules folder                               
                                CopyFile(pluginName, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetModulesRoot));
                                break;
                            //case "clientscripts":// copy files to the site client scripts folder                                
                            //    CopyFile(pluginName, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetClientScriptsRoot));
                            //    break;
                            case "resources":// copy files to the site resources folder                                
                                CopyFile(pluginName, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetResourcesRoot));
                                break;
                            //case "themescommon":// copy files to the site themes common folder                                
                            //    CopyFile(pluginName, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetThemesCommonRoot));
                            //    break;
                            case "adminpages":// copy files to the admin / pages folder                                
                                CopyFile(pluginName, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetAdminPagesRoot));
                                break;
                            case "bin":// copy files to the site bin folder
                                if (fileNode.InnerText.EndsWith(".dll"))
                                {
                                    string binRoot = HostingEnvironment.MapPath(WebRoots.GetBinRoot);
                                    string binfilename = Path.GetFileName(fileNode.InnerText);
                                    string binsourcePath = Path.Combine(directory, binfilename);
                                    // overwrite file if already exists
                                    File.Copy(binsourcePath, binRoot + binfilename, true);
                                }
                                break;
                        }
                    }
                }
                // sql scripts
                XmlNode dataProvidersNode = typeNode["dataproviders"];
                if (dataProvidersNode != null)
                {
                    // find the current active data provider from web.config
                    DataProviderSection section = (DataProviderSection)WebConfigurationManager.GetWebApplicationSection("visinia/DataProvider");
                    string ActiveDataProvider = section.DefaultDataProvider;

                    // find the above stated data provider from the dataproviers
                    // given in the installation file
                    XmlNode activeProvidersNode = dataProvidersNode[ActiveDataProvider.ToLower()];
                    if (activeProvidersNode != null)
                    {
                        XmlNode installScript = activeProvidersNode["install"];
                        if (installScript != null)
                        {
                            // run script
                            string directory = Path.GetDirectoryName(viFile);
                            string scriptPath = Path.Combine(directory, installScript.InnerText);
                            InstallerCommon.ExecuteScript(scriptPath);

                            // put uninstall script in module's uninstall folder
                            XmlNode uninstallScript = activeProvidersNode["uninstall"];
                            if (uninstallScript != null)
                            {
                                string udirectory = Path.GetDirectoryName(viFile);
                                string sourcePath = Path.Combine(udirectory, uninstallScript.InnerText);
                                if (File.Exists(sourcePath))
                                {
                                    // put uninstall script in module's uninstall folder
                                    string uninstallScriptBkup = Path.Combine(UninstallDirectoryName, uninstallScript.InnerText);
                                    string uninstallScriptDirName = Path.GetDirectoryName(uninstallScriptBkup);
                                    if (uninstallScriptDirName.Length > 0)
                                        Directory.CreateDirectory(uninstallScriptDirName);
                                    File.Copy(sourcePath, uninstallScriptBkup, true);
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }

        private static void CopyFile(string pluginName, XmlNode fileNode, string sourcePath, string root)
        {
            string directoryName = root + pluginName;
            string destinationPath = Path.Combine(directoryName, fileNode.InnerText);
            string dirName = Path.GetDirectoryName(destinationPath);
            if (dirName.Length > 0)
                Directory.CreateDirectory(dirName);
            // overwrite file if already exists
            File.Copy(sourcePath, destinationPath, true);
        }
        #endregion

        #region Uninstaller
        internal static bool UnInstallPlugin(int PluginId)
        {
            Plugin installedPlugin = PluginController.GetInstalledPlugin(PluginId);
            if (installedPlugin == null)
                return false;
            
            //NOTE: uninstall all modules relating to this plugin
            List<ModuleInstalled> installedModules = ModulesInstalledController.GetInstalledModules().FindAll(p => string.Compare(p.Package, installedPlugin.Name, true) == 0);
            foreach (ModuleInstalled installedModule in installedModules)
                InstallController.UnInstallModule(installedModule.ModuleInstalledId);   

            // unregister plugin
            PluginController.DeletePlugin(PluginId);            

            string PluginName = installedPlugin.Name;
            string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
            string UninstallFolder = UninstallRoot + PluginName;
            // uninstall folder doesn't exist
            if (!Directory.Exists(UninstallFolder))
                return false;
            // the visinia installtion data file
            string[] vi = Directory.GetFiles(UninstallFolder, "*.vi");
            // more then one installation file
            if ((vi.Length > 1) || (vi.Length < 1))
                return false;
            ParseUnInstallerFile(vi[0], UninstallFolder, PluginName);            

            // delete uninstall folder            
            string PDirectoryName = UninstallRoot + PluginName;
            string[] files = Directory.GetFiles(PDirectoryName, "*.*", SearchOption.AllDirectories);
            // delete package folder if empty
            if (files.Length == 0)
                Directory.Delete(PDirectoryName, true);
            return true;
        }

        private static bool ParseUnInstallerFile(string viFile, string UninstallFolder, string PluginName)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(viFile);
            XmlNode visiniaNode = doc.DocumentElement;
            if (visiniaNode != null)
            {
                // TODO check visinia version 

                // this is plugin installation data
                XmlNode typeNode = visiniaNode["plugin"];
                if (typeNode != null)
                {
                    XmlNode dataNode = typeNode["data"];
                    if (dataNode != null)
                    {
                        // the module must have a name;
                        XmlNode nameNode = dataNode["name"];
                        if (nameNode == null)
                            return false;
                        if (nameNode.InnerText.ToLower() != PluginName.ToLower())
                            return false;                        
                        XmlNode filesNode = typeNode["files"];
                        if (filesNode != null)
                        {
                            foreach (XmlNode fileNode in filesNode.ChildNodes)
                            {
                                // delete files and directories from the proper website folders                                
                                switch (fileNode.Name.ToLower())
                                {
                                    case "modules":// remove files from the site modules folder
                                        string root = HostingEnvironment.MapPath(WebRoots.GetModulesRoot);
                                        string filePath = root + PluginName + "/" + fileNode.InnerText;
                                        if (File.Exists(filePath))
                                            SafeDelete(PluginName, fileNode, root, filePath);
                                        break;
                                    //case "clientscripts":// remove files from the site client scripts folder
                                    //    string scriptsroot = HostingEnvironment.MapPath(WebRoots.GetClientScriptsRoot);
                                    //    string scriptsfilePath = scriptsroot + PluginName + "/" + fileNode.InnerText;
                                    //    if (File.Exists(scriptsfilePath))
                                    //        SafeDelete(PluginName, fileNode, scriptsroot, scriptsfilePath);
                                    //    break;
                                    case "resources":// remove files from the site resources folder
                                        string resourcesroot = HostingEnvironment.MapPath(WebRoots.GetResourcesRoot);
                                        string resourcesfilePath = resourcesroot + PluginName + "/" + fileNode.InnerText;
                                        if (File.Exists(resourcesfilePath))
                                            SafeDelete(PluginName, fileNode, resourcesroot, resourcesfilePath);
                                        break;
                                    //case "themescommon":// remove files from the site themes common folder
                                    //    string themescommonroot = HostingEnvironment.MapPath(WebRoots.GetResourcesRoot);
                                    //    string themescommonfilePath = themescommonroot + PluginName + "/" + fileNode.InnerText;
                                    //    if (File.Exists(themescommonfilePath))
                                    //        SafeDelete(PluginName, fileNode, themescommonroot, themescommonfilePath);
                                    //    break;
                                    case "adminpages":// remove files from the site admin / pages folder
                                        string adminpagesroot = HostingEnvironment.MapPath(WebRoots.GetAdminPagesRoot);
                                        string adminpagesfilePath = adminpagesroot + PluginName + "/" + fileNode.InnerText;
                                        if (File.Exists(adminpagesfilePath))
                                            SafeDelete(PluginName, fileNode, adminpagesroot, adminpagesfilePath);
                                        break;
                                    case "bin":// remove files from the site resources folder
                                        if (fileNode.InnerText.EndsWith(".dll"))
                                        {                                            
                                            string binRoot = HostingEnvironment.MapPath(WebRoots.GetBinRoot);
                                            string binfilename = Path.GetFileName(fileNode.InnerText);
                                            string binfilePath = binRoot + binfilename;
                                            if (File.Exists(binfilePath))
                                                File.Delete(binfilePath);
                                        }
                                        break;
                                }
                            }
                        }
                        // sql scripts
                        XmlNode dataProvidersNode = typeNode["dataproviders"];
                        if (dataProvidersNode != null)
                        {
                            // find the current active data provider from web.config
                            DataProviderSection section = (DataProviderSection)WebConfigurationManager.GetWebApplicationSection("visinia/DataProvider");
                            string ActiveDataProvider = section.DefaultDataProvider;

                            // find the above stated data provider from the dataproviers
                            // given in the installation file
                            XmlNode activeProvidersNode = dataProvidersNode[ActiveDataProvider.ToLower()];
                            if (activeProvidersNode != null)
                            {
                                XmlNode uninstallScript = activeProvidersNode["uninstall"];
                                if (uninstallScript != null)
                                {
                                    string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
                                    string directoryName = UninstallRoot + PluginName;
                                    // run uninstall script
                                    string uninstallScriptBkup = Path.Combine(directoryName, uninstallScript.InnerText);
                                    if (File.Exists(uninstallScriptBkup))
                                        return InstallerCommon.ExecuteScript(uninstallScriptBkup);
                                }
                            }
                        }
                    }
                    return true;
                }
            }
            return false;
        }

        private static bool SafeDelete(string PluginName, XmlNode fileNode, string root, string filePath)
        {
            // delete specified file
            File.Delete(filePath);

            string inner = Path.GetDirectoryName(fileNode.InnerText);
            if (!string.IsNullOrEmpty(inner))
            {
                string innerFolder = root + PluginName + "\\" + inner;

                // don't remove folders if some data is added            
                string[] innerdirs = Directory.GetFileSystemEntries(innerFolder, "*.*");
                if (innerdirs.Length == 0)
                    Directory.Delete(innerFolder, true);
                else
                    return false;
            }
            // delete folder if empty
            string PluginDirectoryName = root + PluginName;
            string[] plugindirs = Directory.GetFileSystemEntries(PluginDirectoryName, "*.*");
            if (plugindirs.Length == 0)
                Directory.Delete(PluginDirectoryName, true);
            else
                return false;            
            return true;
        }
        #endregion
    }
}
